 function C_callbackNIIn(O,Event,ID)
% CALLBACK FOR NI, PROCESSES THE DATA THAT HAVE ARRIVED 
% AND CALLS THE PARADIGM TO PROCESS THE EVENTS

global CG Verbose; 
if isempty(CG.Sessions) return; end
ModuleType = 'NI';
ModuleName = CG.Sessions.NI(ID).Name;

SR = CG.Sessions.NI(ID).SAI.Rate;

% KEEP TRACK OF SAMPLES
if CG.Sessions.NI(ID).Iteration == 0; CG.Sessions.NI(ID).TriggerTime = Event.TriggerTime; end
CG.Sessions.NI(ID).Iteration = CG.Sessions.NI(ID).Iteration + 1; % defined in the C_initialize
NNewPackets = size(Event.Data,1);
cInd = CG.Sessions.NI(ID).PacketsAcquired+ 1 : CG.Sessions.NI(ID).PacketsAcquired + NNewPackets;
CG.Sessions.NI(ID).PacketsAcquired = cInd(end);

% ENLARGE VARIABLES IN BIG STEPS (IF NECESSARY)
if CG.Sessions.NI(ID).CurrentSize < CG.Sessions.NI(ID).PacketsAcquired 
  NewSize = CG.Sessions.NI(ID).CurrentSize + CG.Parameters.NI.StepSize;
  CG.Data.NI(ID).Analog(NewSize,1:size(Event.Data,2)) = 0; % EnlargeArray
  CG.Data.NI(ID).Time(NewSize,1) = 0; % EnlargeArray
  CG.Sessions.NI(ID).CurrentSize = NewSize;
end

% MOVE DATA TO INTERNAL VARIABLE
CG.Data.NI(ID).Analog(cInd,1:size(Event.Data,2)) = Event.Data;
CG.Data.NI(ID).Time(cInd,1) = Event.TimeStamps;

% TRANSFER PACKETS TO INTERNAL VARIABLES
CG.Sessions.NI(ID).PacketsThisIteration = NNewPackets;
CG.Sessions.NI(ID).TimeThisIteration = Event.TimeStamps(end) - CG.Sessions.NI(ID).LastTime;
CG.Sessions.NI(ID).LastTime = CG.Data.NI(ID).Time(cInd(end),1);
  
%% DETECT EVENTS
Events = struct('Name',{},'Data',{},'Time',{});

% CHECK FOR STATE CHANGES ON THE ANALOG CHANNELS
ThreshCh = ~isnan(CG.Sessions.NI(ID).AnalogThresholds);
DiffSteps = 5;
if any(ThreshCh)
  NThreshCh = sum(ThreshCh);
  cIndE = [cInd(1)-DiffSteps:cInd(1)-1,cInd];
  if CG.Sessions.NI(ID).Iteration == 1
    cAnalog = [repmat(CG.Data.NI(ID).Analog(cInd(1),ThreshCh),[DiffSteps,1]);CG.Data.NI(ID).Analog(cInd,ThreshCh)];
  else
    cAnalog = CG.Data.NI(ID).Analog([cInd(1)-DiffSteps:cInd(1)-1,cInd],ThreshCh);
  end
  
  Thresholds = CG.Sessions.NI(ID).AnalogThresholds(ThreshCh);
  cAnalog = bsxfun(@gt,cAnalog,Thresholds);
  ThreshChNums = find(ThreshCh);
  cDiffAnalog = diff(cAnalog,1,1);
  [iT,iD] = find(cDiffAnalog);
  
  % COLLECT ALL EVENTS
  if ~isempty(iT)
    for iE = 1:length(iD)
      cPos = cIndE(iT(iE));
      cEventTime = CG.Data.NI(ID).Time(cPos,1);
      cEventTime = cEventTime+(DiffSteps-1)/SR;
      Events(iE).Time = [cEventTime,cPos+DiffSteps-1,cEventTime/86400 + CG.Sessions.NI(ID).TriggerTime ]; % Event Time
      Events(iE).Data = ThreshChNums(iD(iE)); % Channel Number
      if cDiffAnalog(iT(iE),iD(iE))>0;     Events(iE).Name = 'AI_to_High';
      else                                                   Events(iE).Name = 'AI_to_Low';
      end
    end
    
    % FILTER OUT PAIRS OF SUPERSHORT UP-DOWN EVENTS
    EventDurations = diff(iT);
    cInd2 = find(EventDurations<DiffSteps); % 0.5ms non detection
    iEC = 0; iE=1;
    DeleteInd = zeros(1,2*length(cInd2));
    while iE<=length(cInd2)
      if (Events(cInd2(iE)).Data == Events(cInd2(iE)+1).Data)
        iEC = iEC + 2;
        DeleteInd(iEC-1:iEC) = [cInd2(iE),cInd2(iE)+1];
        if length(cInd2)>iE && cInd2(iE+1) == cInd2(iE)+1
          iE=iE+1; % SKIP TO THE NEXT IF THE TWO ARE NEIGHBORS
        end
        %disp(['Filtered out Channel : ',n2s(Events(cInd(iE)).Data)]);
      end
      iE = iE+1;
    end
    if iEC>0
      DeleteInd = DeleteInd(1:iEC);
      SelInd = setdiff([1:length(iD)],DeleteInd);
      Events = Events(SelInd);
    end
    
    % FILTER OUT EVENTS WHICH ARE TOO CLOSE IN TIME
    %    if ~isempty(Events)
    %      LastEventTime = -1; SelInd = zeros(size(Events)); iEC = 0;
    %      for iE=1:length(Events)
    %        if Events(iE).Time(1) - LastEventTime > 0.005
    %          iEC = iEC + 1;
    %          SelInd(iEC) = iE;
    %        end
    %      end
    %      Events = Events(SelInd(1:iEC));
    %    end
    %
    if ~isempty(Events)
      % PROCESS EVENTS
      for iE=1:length(Events)
        CG.Paradigm.processEvent(ModuleName,Events(iE));
      end
      
      % RECORD TIMING AND EVENTS
      C_addEvents(ModuleType,ID,Events);
    end
  end
end
  
 %% UPDATE ARDUINO DISPLAY
 if CG.Display.NI(ID).State C_updateNIDisplay(ID); end

